<!DOCTYPE html>
<html>
<head>
  <meta charset="utf-8">
  <title>video.js HLS Stats</title>

  <link href="../../node_modules/video.js/dist/video-js.css" rel="stylesheet">

  <!-- video.js -->
  <script src="../../node_modules/video.js/dist/alt/video.core.js"></script>

  <!-- EME (Encrypted Media Extensions) plugin -->
  <script src="../../node_modules/videojs-contrib-eme/dist/videojs-contrib-eme.js"></script>

  <!-- HLS plugin -->
  <script src="../../dist/videojs-http-streaming.js"></script>

  <!-- quality levels plugin -->
  <script src="../../node_modules/videojs-contrib-quality-levels/dist/videojs-contrib-quality-levels.js"></script>

  <!-- player stats visualization -->
  <link href="stats.css" rel="stylesheet">
  <script src="../../node_modules/d3/d3.min.js"></script>

  <style>
    body {
      font-family: Arial, sans-serif;
      margin: 20px;
    }
    .info {
      background-color: #eee;
      border: thin solid #333;
      border-radius: 3px;
      padding: 0 5px;
      margin: 20px 0;
    }
    input {
      margin-top: 15px;
      min-width: 450px;
      padding: 5px;
    }
  </style>

</head>
<body>
  <div class="info">
    <p>The video below is an <a href="https://developer.apple.com/library/ios/documentation/networkinginternet/conceptual/streamingmediaguide/Introduction/Introduction.html#//apple_ref/doc/uid/TP40008332-CH1-SW1">HTTP Live Stream</a>.</p>
  </div>

  <div class="container">
    <div id="fixture"></div>
    <ul id="segment-metadata"></ul>
  </div>

  <form id="load-url">
    <label>
      URL to Load:
      <input id="url-to-load" type="url"
      value="https://d2zihajmogu5jn.cloudfront.net/bipbop-advanced/bipbop_16x9_variant.m3u8"
      >
    </label>
    <br>
    <label>
      Player Options:
      <input id="player-options" type="text" value='{"plugins": {"eme": {}}}'>
    </label>
    <br>
    <label>
      URL Content Type:
      <input id="url-content-type" type="text" value="application/x-mpegURL">
    </label>
    <br>
    <label>
      Default Caption Track Label (blank for none):
      <input id="caption-track" type="text" value="">
    </label>
    <br>
    <label>
      Technology Mode:
      <input type="radio" name="technology-mode" value="auto" checked> Auto
      <input type="radio" name="technology-mode" value="html"> HTML
      <input type="radio" name="technology-mode" value="flash"> Flash
    </label>
    <br>
    <label>
      Autoplay:
      <input type="radio" name="autoplay" value="on"> On
      <input type="radio" name="autoplay" value="off" checked> Off
    </label>
    <br>
    <label>
      Live UI:
      <input type="radio" name="liveui" value="on" checked> On
      <input type="radio" name="liveui" value="off"> Off
    </label>
    <br>
    <label>
      Override Native:
      <input type="radio" name="override-native" value="on"> On
      <input type="radio" name="override-native" value="off" checked> Off
    </label>

    <br>
    <br>
    <button type="submit">Load</button>
  </form>
  <br>

  <section class="stats">
    <div class="player-stats">
      <h2>Player Stats</h2>
      <dl>
        <dt>Current Time:</dt>
        <dd class="current-time-stat">0</dd>
        <dt>Buffered:</dt>
        <dd class="buffered-stat">-</dd>
        <dt>Video Buffered:</dt>
        <dd class="video-buffered-stat">-</dd>
        <dt>Audio Buffered:</dt>
        <dd class="audio-buffered-stat">-</dd>
        <dt>Seekable:</dt>
        <dd><span class="seekable-start-stat">-</span> - <span class="seekable-end-stat">-</span></dd>
        <dt>Video Bitrate:</dt>
        <dd class="video-bitrate-stat">0 kbps</dd>
        <dt>Measured Bitrate:</dt>
        <dd class="measured-bitrate-stat">0 kbps</dd>
        <dt>Video Timestamp Offset</dt>
        <dd class="video-timestampoffset">0</dd>
        <dt>Audio Timestamp Offset</dt>
        <dd class="audio-timestampoffset">0</dd>
      </dl>
    </div>
    <div class="event-counts">
      <h2>Event Counts</h2>
      <dl>
        <dt>Play:</dt>
        <dd class="play-count">0</dd>
        <dt>Playing:</dt>
        <dd class="playing-count">0</dd>
        <dt>Seeking:</dt>
        <dd class="seeking-count">0</dd>
        <dt>Seeked:</dt>
        <dd class="seeked-count">0</dd>
      </dl>
    </div>
    <div id="quality-levels">
      <h2 title="Click on the buttons to toggle the level">Quality Levels:</h2>
      <ul class="quality-buttons">
      </ul>
    </div>
    <h3 class="bitrate-switching">Bitrate Switching</h3>
    <div class="switching-stats">
      Once the player begins loading, you'll see information about the
      operation of the adaptive quality switching here.
    </div>
    <h3>Timed Metadata</h3>
    <div class="segment-timeline"></div>
  </section>

  <script src="stats.js"></script>
  <script>
    function createQualityButton(qualityLevel, parent) {
      var li = document.createElement('li');
      var button = document.createElement('button');
      var classes = button.classList;

      if (qualityLevel.enabled) {
        classes.add('enabled');
      } else {
        classes.add('disabled');
      }

      button.innerHTML = qualityLevel.id + ': ' + qualityLevel.bitrate + ' kbps';
      button.id = 'quality-level-' + qualityLevel.id;
      button.title = "Click to toggle";

      button.onclick = function() {
        var old = qualityLevel.enabled;
        qualityLevel.enabled = !old;
        button.classList.toggle('enabled');
        button.classList.toggle('disabled');
      }
      li.appendChild(button);
      parent.appendChild(li);
    }

    function getCheckedValue(name) {
      var radios = document.getElementsByName(name);
      var value;
      for (var i = 0, length = radios.length; i < length; i++) {
        if (radios[i].checked) {
          value = radios[i].value;
          break;
        }
      }
      return value;
    }

    function createPlayer(cb) {
      if (window.stats_timer) {
        clearInterval(window.stats_timer);
      }
      // dispose of existing player
      if(window.player) {
        window.player.dispose();
      }

      var qualityButtons = document.querySelector('.quality-buttons');

      qualityButtons.innerHTML = '';

      // create video element in the dom
      var video = document.createElement('video');
      video.id = 'videojs-http-streaming-player';
      video.className = 'video-js vjs-default-skin';
      video.setAttribute('controls', true);
      video.setAttribute('height', 300);
      video.setAttribute('width', 600);
      document.querySelector('#fixture').appendChild(video);
      var techRadios = document.getElementsByName('technology-mode');
      var techMode = getCheckedValue('technology-mode') || 'auto';
      var autoplay = getCheckedValue('autoplay') || 'off';
      var liveui = getCheckedValue('liveui') || 'off';
      var overrideNative = getCheckedValue('override-native') || 'off';
      var captionTrack = document.getElementById('caption-track').value || "";
      var url = document.getElementById('url-to-load').value || "";
      var options = {};
      // try to parse options from the form
      try {
        options = JSON.parse(document.getElementById('player-options').value);
      } catch(err) {
        console.log("Reseting options to {}, JSON Parse Error:", err);
      }
      if(typeof options.techOrder === 'undefined') {
        if (techMode === 'html') {
          options.techOrder = ['html5'];
        } else if (techMode === 'flash') {
          options.techOrder = ['flash'];
        }
      }
      if(typeof options.autoplay === 'undefined') {
        if (autoplay === 'on') {
          options.autoplay = true;
        } else if (autoplay === 'off') {
          options.autoplay = false;
        }
      }
      if(typeof options.liveui === 'undefined') {
        if (liveui === 'on') {
          options.liveui = true;
        } else if (liveui === 'off') {
          options.liveui = false;
        }
      }

      videojs.options.vhs.overrideNative = overrideNative === 'on';

      var type = document.getElementById('url-content-type').value;
      // use the form data to add a src to the player
      try {
        window.player = videojs(video.id, options);
        if(captionTrack) {
          // hackey way to show captions
          window.player.on('loadeddata', function(event) {
            textTracks = this.textTracks().tracks_;
            for(var i = 0; i < textTracks.length; i++) {
              if(textTracks[i].label === captionTrack) {
                console.log("Found matching track, going to show " + captionTrack);
                textTracks[i].mode = "showing";
                break;
              }
            }
          });
        }
        window.player.src({
          src: url,
          type: type
        });

        cb(player);
      } catch(err) {
        console.log("caught an error trying to create and add src to player:", err);
      }
    }

    function getBuffered(buffered) {
      var bufferedText = '';

      if (!buffered) {
        return bufferedText;
      }

      if (buffered.length) {
        bufferedText += buffered.start(0) + ' - ' + buffered.end(0);
      }
      for (i = 1; i < buffered.length; i++) {
        bufferedText += ', ' + buffered.start(i) + ' - ' + buffered.end(i);
      }
      return bufferedText;
    }

    function setup(player) {
      player.ready(function() {

        // ------------
        // Player Stats
        // ------------

        var currentTimeStat = document.querySelector('.current-time-stat');
        var bufferedStat = document.querySelector('.buffered-stat');
        var videoBufferedStat = document.querySelector('.video-buffered-stat');
        var audioBufferedStat = document.querySelector('.audio-buffered-stat');
        var seekableStartStat = document.querySelector('.seekable-start-stat');
        var seekableEndStat = document.querySelector('.seekable-end-stat');
        var videoBitrateState = document.querySelector('.video-bitrate-stat');
        var measuredBitrateStat = document.querySelector('.measured-bitrate-stat');
        var videoTimestampOffset = document.querySelector('.video-timestampoffset');
        var audioTimestampOffset = document.querySelector('.audio-timestampoffset');
        var qualityButtons = document.querySelector('.quality-buttons');

        player.on('timeupdate', function() {
          currentTimeStat.textContent = player.currentTime().toFixed(1);
        });

        window.stats_timer = window.setInterval(function() {
          var bufferedText = '', oldStart, oldEnd, i;

          // buffered
          bufferedStat.textContent = getBuffered(player.buffered());
          videoBufferedStat.textContent = getBuffered(
            player.tech(true).vhs.playlistController_.mainSegmentLoader_.sourceUpdater_.videoBuffer &&
            player.tech(true).vhs.playlistController_.mainSegmentLoader_.sourceUpdater_.videoBuffer.buffered
          );

          // demuxed audio
          var audioBuffer = getBuffered(
            player.tech(true).vhs.playlistController_.audioSegmentLoader_.sourceUpdater_.audioBuffer &&
            player.tech(true).vhs.playlistController_.audioSegmentLoader_.sourceUpdater_.audioBuffer.buffered
          );

          // muxed audio
          if (!audioBuffer) {
            audioBuffer = getBuffered(
              player.tech(true).vhs.playlistController_.mainSegmentLoader_.sourceUpdater_.audioBuffer &&
              player.tech(true).vhs.playlistController_.mainSegmentLoader_.sourceUpdater_.audioBuffer.buffered
            );
          }
          audioBufferedStat.textContent = audioBuffer;

          if (player.tech(true).vhs.playlistController_.audioSegmentLoader_.sourceUpdater_.audioBuffer) {
            audioTimestampOffset.textContent = player.tech(true).vhs.playlistController_.audioSegmentLoader_.sourceUpdater_.audioBuffer.timestampOffset;
          } else if (player.tech(true).vhs.playlistController_.mainSegmentLoader_.sourceUpdater_.audioBuffer) {
            audioTimestampOffset.textContent = player.tech(true).vhs.playlistController_.mainSegmentLoader_.sourceUpdater_.audioBuffer.timestampOffset;
          }

          if (player.tech(true).vhs.playlistController_.mainSegmentLoader_.sourceUpdater_.videoBuffer) {
            videoTimestampOffset.textContent = player.tech(true).vhs.playlistController_.mainSegmentLoader_.sourceUpdater_.videoBuffer.timestampOffset;
          }

          // seekable
          var seekable = player.seekable();
          if (seekable && seekable.length) {

            oldStart = seekableStartStat.textContent;
            if (seekable.start(0).toFixed(1) !== oldStart) {
              seekableStartStat.textContent = seekable.start(0).toFixed(1);
            }
            oldEnd = seekableEndStat.textContent;
            if (seekable.end(0).toFixed(1) !== oldEnd) {
              seekableEndStat.textContent = seekable.end(0).toFixed(1);
            }
          }

          // bitrates
          var playlist = player.tech_.vhs.playlists.media();
          if (playlist && playlist.attributes && playlist.attributes.BANDWIDTH) {
            videoBitrateState.textContent = (playlist.attributes.BANDWIDTH / 1024).toLocaleString(undefined, {
              maximumFractionDigits: 1
            }) + ' kbps';
          }
          if (player.tech_.vhs.bandwidth) {
            measuredBitrateStat.textContent = (player.tech_.vhs.bandwidth / 1024).toLocaleString(undefined, {
              maximumFractionDigits: 1
            }) + ' kbps';
          }
        }, 100);

        var trackEventCount = function(eventName, selector) {
          var count = 0, element = document.querySelector(selector);
          player.on(eventName, function() {
            count++;
            element.innerHTML = count;
          });
        };
        trackEventCount('play', '.play-count');
        trackEventCount('playing', '.playing-count');
        trackEventCount('seeking', '.seeking-count');
        trackEventCount('seeked', '.seeked-count');

        videojs.Vhs.displayStats(document.querySelector('.switching-stats'), player);
        videojs.Vhs.displayCues(document.querySelector('.segment-timeline'), player);

        // setup quality levels
        var qualityLevels = player.qualityLevels();

        qualityLevels.on('addqualitylevel', function(event) {
          createQualityButton(event.qualityLevel, qualityButtons);
        });

        qualityLevels.on('change', function(event) {
          for (var i = 0; i < qualityLevels.length; i++) {
            var level = qualityLevels[i];
            var button = document.getElementById('quality-level-' + level.id);

            button.classList.remove('selected');
          }

          var selected = qualityLevels[event.selectedIndex];
          var button = document.getElementById('quality-level-' + selected.id);
          button.classList.add('selected');
        });

        // setup segment metadata
        var segmentMetadata = document.querySelector('#segment-metadata');

        player.one('loadedmetadata', function() {
          var tracks = player.textTracks();
          var segmentMetadataTrack;
          var i = 0;

          for (i = 0; i < tracks.length; i++) {
            if (tracks[i].label === 'segment-metadata') {
              segmentMetadataTrack = tracks[i];
            }
          }

          while (segmentMetadata.children.length) {
            segmentMetadata.removeChild(segmentMetadata.firstChild);
          }

          if (segmentMetadataTrack) {
            segmentMetadataTrack.addEventListener('cuechange', function() {
              var cues = segmentMetadataTrack.activeCues || [];
              var i;

              while (segmentMetadata.children.length) {
                segmentMetadata.removeChild(segmentMetadata.firstChild);
              }

              for (i = 0; i < cues.length; i++) {
                var text = JSON.stringify(JSON.parse(cues[i].text), null, 2);

                var li = document.createElement('li')
                var pre = document.createElement('pre');
                pre.textContent = text;
                li.appendChild(pre);
                segmentMetadata.appendChild(li);
              }

            });
          }
        });

      });
    }

    (function(window, videojs) {
      if (videojs.log.debug) {
        videojs.log.level('debug');
      }
      createPlayer(setup);
      // hook up the video switcher
      document.getElementById('load-url').addEventListener('submit', function(event) {
        event.preventDefault();
        createPlayer(setup);
        return false;
      });
    }(window, window.videojs));
  </script>
</body>
</html>
